#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Odoo JSON-RPC Client - Read Hostel Students
使用 API Key 从 Odoo 读取学生数据
"""

import json
import requests
import sys
import os
from datetime import datetime

# 禁用 SSL 警告
requests.packages.urllib3.disable_warnings()

# 获取脚本所在目录
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
LOG_DIR = os.path.join(SCRIPT_DIR, 'json_logs')

# 创建日志目录
os.makedirs(LOG_DIR, exist_ok=True)

# 全局日志列表
json_rpc_logs = []

# Odoo 服务器配置
ODOO_URL = "https://naturedao.tech"
API_KEY = "298a4b1128cb1bdb8da2bb87e0e9c11e9b82850f"
DATABASE = "odootest"
USERNAME = "yan.gao@ccwin-in.com"  # API Key 对应的用户名（登录邮箱）


def save_json_logs():
    """保存所有 JSON-RPC 调用日志到文件"""
    timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
    log_file = os.path.join(LOG_DIR, f'json_rpc_log_{timestamp}.json')
    
    log_data = {
        'timestamp': datetime.now().isoformat(),
        'server': ODOO_URL,
        'database': DATABASE,
        'username': USERNAME,
        'total_calls': len(json_rpc_logs),
        'calls': json_rpc_logs
    }
    
    with open(log_file, 'w', encoding='utf-8') as f:
        json.dump(log_data, f, ensure_ascii=False, indent=2)
    
    print(f"\n📝 JSON-RPC 调用日志已保存到: {log_file}")
    return log_file

def authenticate():
    """使用 API Key 获取用户 ID"""
    print("正在使用 API Key 进行身份验证...")
    print(f"  数据库: {DATABASE}")
    print(f"  用户名: {USERNAME}")
    
    url = f"{ODOO_URL}/jsonrpc"
    
    payload = {
        "jsonrpc": "2.0",
        "method": "call",
        "params": {
            "service": "common",
            "method": "authenticate",
            "args": [DATABASE, USERNAME, API_KEY, {}]
        },
        "id": 1
    }
    
    try:
        request_time = datetime.now().isoformat()
        response = requests.post(url, json=payload, verify=False)
        response.raise_for_status()
        
        result = response.json()
        response_time = datetime.now().isoformat()
        
        # 记录 JSON-RPC 调用
        log_entry = {
            'call_number': len(json_rpc_logs) + 1,
            'method': 'authenticate',
            'request_time': request_time,
            'response_time': response_time,
            'url': url,
            'request': {
                'service': 'common',
                'method': 'authenticate',
                'args': [DATABASE, USERNAME, '***API_KEY_HIDDEN***', {}]  # 隐藏 API Key
            },
            'response': result,
            'status': 'success' if result.get('result') else 'failed'
        }
        json_rpc_logs.append(log_entry)
        
        # 调试：打印完整响应（如果需要调试，取消注释下一行）
        # print(f"  响应: {json.dumps(result, indent=2)}")
        
        if 'error' in result:
            print(f"✗ 认证错误: {result['error']}")
            return None
        
        uid = result.get('result')
        
        if uid:
            print(f"✓ 认证成功！用户 ID: {uid}")
            return uid
        else:
            print("✗ 认证失败：返回结果为 False 或 None")
            print("  可能原因：")
            print("  - API Key 已过期或被撤销")
            print("  - 用户名与 API Key 不匹配")
            print("  - 数据库名称错误")
            return None
    
    except requests.exceptions.RequestException as e:
        # 记录错误
        log_entry = {
            'call_number': len(json_rpc_logs) + 1,
            'method': 'authenticate',
            'request_time': request_time,
            'url': url,
            'request': payload,
            'error': str(e),
            'status': 'error'
        }
        json_rpc_logs.append(log_entry)
        
        print(f"✗ 请求错误: {e}")
        return None


def call_odoo(uid, model, method, args=None, kwargs=None):
    """
    调用 Odoo 的 JSON-RPC API
    
    Args:
        uid: 用户 ID
        model: 模型名称
        method: 方法名
        args: 位置参数
        kwargs: 关键字参数
    
    Returns:
        响应结果
    """
    if args is None:
        args = []
    if kwargs is None:
        kwargs = {}
    
    url = f"{ODOO_URL}/jsonrpc"
    
    payload = {
        "jsonrpc": "2.0",
        "method": "call",
        "params": {
            "service": "object",
            "method": "execute_kw",
            "args": [
                DATABASE,
                uid,  # 使用用户 ID 而不是用户名
                API_KEY,
                model,
                method,
                args,
                kwargs
            ]
        },
        "id": 1
    }
    
    try:
        print(payload)
        request_time = datetime.now().isoformat()
        response = requests.post(url, json=payload, verify=False)
        response.raise_for_status()
        
        result = response.json()
        response_time = datetime.now().isoformat()
        
        # 记录 JSON-RPC 调用
        log_entry = {
            'call_number': len(json_rpc_logs) + 1,
            'method': f'{model}.{method}',
            'request_time': request_time,
            'response_time': response_time,
            'url': url,
            'request': {
                'service': 'object',
                'method': 'execute_kw',
                'model': model,
                'odoo_method': method,
                'args': args,
                'kwargs': kwargs
            },
            'response': {
                'success': 'error' not in result,
                'result_preview': str(result.get('result'))[:200] if result.get('result') else None,  # 只保存前200字符
                'error': result.get('error') if 'error' in result else None,
                'record_count': len(result.get('result', [])) if isinstance(result.get('result'), list) else None
            },
            'status': 'success' if 'error' not in result else 'failed'
        }
        json_rpc_logs.append(log_entry)
        
        if 'error' in result:
            print(f"错误: {result['error']}")
            return None
        
        return result.get('result')
    
    except requests.exceptions.RequestException as e:
        # 记录错误
        log_entry = {
            'call_number': len(json_rpc_logs) + 1,
            'method': f'{model}.{method}',
            'request_time': request_time,
            'url': url,
            'request': {
                'model': model,
                'method': method,
                'args': args,
                'kwargs': kwargs
            },
            'error': str(e),
            'status': 'error'
        }
        json_rpc_logs.append(log_entry)
        
        print(f"请求错误: {e}")
        return None


def get_students(uid):
    """读取学生数据"""
    print("正在读取学生数据...")
    
    # 定义要读取的字段
    fields = [
        'id', 'name', 'gender', 'class_id', 'room_id', 'hostel_id',
        'check_in_date', 'check_out_date', 'stay_duration', 'status', 'active'
    ]
    
    # 调用 search_read 方法
    students = call_odoo(
        uid,  # 传递用户 ID
        'hostel.student',
        'search_read',
        args=[[]],  # domain: [] 表示所有记录
        kwargs={
            'fields': fields,
            'limit': 100,
            'order': 'id desc'
        }
    )
    
    return students


def print_students(students):
    """格式化打印学生数据"""
    if not students:
        print("\n没有找到任何学生记录。")
        return
    
    print(f"\n找到 {len(students)} 条学生记录：")
    print("=" * 130)
    
    # 表头
    header = (
        f"{'ID':<6} {'姓名':<20} {'性别':<8} {'班级':<18} "
        f"{'房间':<22} {'状态':<12} {'入住日期':<12} {'住宿天数':<10}"
    )
    print(header)
    print("-" * 130)
    
    # 数据行
    for student in students:
        # 处理 Many2one 字段
        room_name = student.get('room_id')[1] if student.get('room_id') else "未分配"
        
        # 性别翻译
        gender_map = {'male': '男', 'female': '女', 'other': '其他'}
        gender = gender_map.get(student.get('gender', ''), student.get('gender', ''))
        
        # 状态翻译
        status_map = {
            'draft': '草稿',
            'reservation': '预订',
            'pending': '待定',
            'paid': '已付款',
            'discharge': '已退房',
            'cancel': '已取消'
        }
        status = status_map.get(student.get('status', ''), student.get('status', ''))
        
        # 日期和天数
        check_in = student.get('check_in_date', '') or ''
        duration = student.get('stay_duration', 0)
        
        # 打印行
        row = (
            f"{student.get('id', ''):<6} "
            f"{student.get('name', ''):<20} "
            f"{gender:<8} "
            f"{student.get('class_id', ''):<18} "
            f"{room_name:<22} "
            f"{status:<12} "
            f"{check_in:<12} "
            f"{duration:<10}"
        )
        print(row)
    
    print("=" * 130)


def print_student_details(students, limit=5):
    """打印详细信息"""
    print(f"\n\n详细信息（前 {min(limit, len(students))} 条）：")
    print("=" * 80)
    
    for i, student in enumerate(students[:limit], 1):
        print(f"\n【学生 {i}】")
        print(f"  ID: {student.get('id')}")
        print(f"  姓名: {student.get('name')}")
        print(f"  性别: {student.get('gender')}")
        print(f"  班级: {student.get('class_id')}")
        
        room_info = student.get('room_id')
        print(f"  房间: {room_info[1] if room_info else '未分配'}")
        
        hostel_info = student.get('hostel_id')
        print(f"  宿舍: {hostel_info[1] if hostel_info else '未分配'}")
        
        print(f"  入住日期: {student.get('check_in_date', '未设置')}")
        print(f"  退房日期: {student.get('check_out_date', '未设置')}")
        print(f"  住宿天数: {student.get('stay_duration', 0)} 天")
        print(f"  状态: {student.get('status')}")
        print(f"  是否激活: {'是' if student.get('active') else '否'}")
        print("-" * 80)
    
    if len(students) > limit:
        print(f"\n（共 {len(students)} 条记录，只显示前 {limit} 条详细信息）")


def print_statistics(students):
    """打印统计信息"""
    if not students:
        return
    
    print("\n\n统计信息：")
    print("=" * 80)
    
    # 性别统计
    gender_count = {}
    for student in students:
        gender = student.get('gender', 'unknown')
        gender_count[gender] = gender_count.get(gender, 0) + 1
    
    print("性别分布：")
    gender_map = {'male': '男', 'female': '女', 'other': '其他'}
    for gender, count in gender_count.items():
        print(f"  {gender_map.get(gender, gender)}: {count} 人")
    
    # 状态统计
    status_count = {}
    for student in students:
        status = student.get('status', 'unknown')
        status_count[status] = status_count.get(status, 0) + 1
    
    print("\n状态分布：")
    status_map = {
        'draft': '草稿', 'reservation': '预订', 'pending': '待定',
        'paid': '已付款', 'discharge': '已退房', 'cancel': '已取消'
    }
    for status, count in status_count.items():
        print(f"  {status_map.get(status, status)}: {count} 人")
    
    # 房间分配统计
    with_room = sum(1 for s in students if s.get('room_id'))
    without_room = len(students) - with_room
    
    print(f"\n房间分配：")
    print(f"  已分配: {with_room} 人")
    print(f"  未分配: {without_room} 人")
    
    print("=" * 80)


def main():
    """主函数"""
    print("=" * 80)
    print("Odoo JSON-RPC Client - 读取宿舍学生数据")
    print("=" * 80)
    print(f"服务器: {ODOO_URL}")
    print(f"数据库: {DATABASE}")
    print(f"用户名: {USERNAME}")
    print(f"API Key: {API_KEY[:20]}...{API_KEY[-10:]}")
    print("=" * 80)
    
    try:
        # 1. 先用 API Key 进行认证获取 UID
        uid = authenticate()
        
        if not uid:
            print("\n✗ 认证失败，无法继续。")
            print("\n提示：")
            print("1. 检查 API Key 是否正确")
            print("2. 尝试重新生成 API Key")
            print("3. 或使用用户名+密码方式：")
            print("   python3 read_students_simple.py admin your_password")
            return
        
        # 2. 读取学生数据
        students = get_students(uid)
        
        if not students:
            print("\n没有找到任何学生记录。")
            return
        
        # 3. 打印数据
        print_students(students)
        print_statistics(students)
        print_student_details(students, limit=3)
        
        print("\n✓ 数据读取完成！")
        print(f"总记录数: {len(students)}")
        
        # 4. 保存 JSON-RPC 调用日志
        log_file = save_json_logs()
        print(f"日志文件大小: {os.path.getsize(log_file) / 1024:.2f} KB")
        
    except Exception as e:
        print(f"\n✗ 错误: {str(e)}")
        import traceback
        traceback.print_exc()
        
        # 即使出错也保存日志
        if json_rpc_logs:
            save_json_logs()


if __name__ == "__main__":
    main()

